/*
* Copyright 2009 NOR_/D Labs <http://labs.nortd.com>
*
* This file is part of SceneExpression.
* SceneExpression is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License 
* as published by the Free Software Foundation, either version 3 of 
* the License, or (at your option) any later version. For details
* see <http://www.gnu.org/licenses/>.
*
* * *
* The sxScene is one of the most central classes.
* It embodies the viewer, camera, and scene graph root.
* Per application there is usually one instance as part of
* the sxApp derived class of which there is one instance in main().
* sxScene and sxApp work together. All the standard events
* (mouse, keyboard, resize) are send from the scene's viewer to
* the sxApp. Latter being the super global, grand-central-like
* event handler and where (the subclass of sxApp) all the user 
* code goes.
*
*/

#ifndef SX_SCENE
#define SX_SCENE

#include <osg/Geode>
#include <osg/Geometry>
#include <osg/MatrixTransform>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osg/Math>
#include <osg/Vec3>

#include "sxConstants.h"
#include "sxTypes.h"
#include "sxVisitors.h"
#include "sxEvents.h"


class sxNode;
class sxWidget;



class sxApp : public osgGA::GUIEventHandler,
              public sxKeyHandler, public sxMouseHandler, 
              public sxTouchHandler, public sxPenHandler,
              public sxButtonHandler, public sxSliderHandler, public sxCheckboxHandler  {
    public:
  
        sxScene*  scene;
  
        sxApp();
        ~sxApp();
        
        virtual void setup() {}
        virtual void update() {}
        virtual void exit() {}
        virtual void resize( int w, int h ) {}

        virtual void keyPress( sxKey& key ) {}
        virtual void keyRelease( sxKey& key ) {}
        
        virtual void mousePress( sxMouse& key ) {}
        virtual void mouseRelease( sxMouse& key ) {}
        virtual void mouseMove( sxMouse& key ) {}
        virtual void mouseDrag( sxMouse& key ) {}
        virtual void mouseScroll( sxMouse& key ) {}
        virtual void mouseDoubleclick( sxMouse& key ) {}
        
        virtual void touchDown( sxTouch& touch ) {}
        virtual void touchUp( sxTouch& touch ) {}
        virtual void touchMove( sxTouch& touch ) {}
        
        virtual void penPressure( sxPen& pen ) {}
        virtual void penOrientation( sxPen& pen ) {}
        virtual void penEnter( sxPen& pen ) {}
        virtual void penLeave( sxPen& pen ) {}

        virtual void widgetButton( sxButtonWidget& button ) {}
        virtual void widgetSlider( sxSliderWidget& slider ) {}
        virtual void widgetCheckbox( sxCheckboxWidget& checkbox ) {}    
    
                
        virtual int run();
        virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&);
        virtual void accept(osgGA::GUIEventHandlerVisitor& v)   { v.visit(*this); };
    
    protected:
        sxKey    _keyEvent;
        sxMouse  _mouseEvent;
        sxPen    _penEvent;
        
};



class sxScene {

    public:
      
        sxScene();
        virtual ~sxScene() {};

        int run();
        
        
        // Accessors
        //    
        int getWidth() { return _width; }
        int getHeight() { return _height; }
        void setBackgroundColor( const osg::Vec4& color );
        void setBackgroundColor( float r, float g, float b, float a=1.0f );
        osg::Vec3 getBackgroundColor();
        void setBackgroundAuto( bool bAuto );
        int getFrameNumber();
        double getSimulationTime();  //in sec from app launch
        void enableDefaultLighting();
        void disableDefaultLighting();
        

        // Scene Graph Hierarchy
        //
        virtual bool addChild( sxNode* node );
        virtual bool insertChild( unsigned int index, sxNode* node );
        virtual bool removeChild( sxNode* node );
        virtual bool replaceChild( sxNode* origNode, sxNode* newNode );
        unsigned int getNumChildren();
        virtual bool setChild( unsigned int index, sxNode* node );
        sxNode*      getChild( unsigned int index );
        bool         containsNode( sxNode* node );
        unsigned int getChildIndex( sxNode* node );
        
        // Scene Graph Geometry
        //
        virtual bool addDrawable( sxDrawable* drawable );
        virtual bool removeDrawable( sxDrawable* drawable );
        virtual bool replaceDrawable( sxDrawable* origDrawable, sxDrawable* newDrawable );
        unsigned int getNumDrawables();
        virtual bool setDrawable( unsigned int index, sxDrawable* drawable );
        sxDrawable*  getDrawable( unsigned int index );
        bool         containsDrawable( sxDrawable* drawable );
        unsigned int getDrawableIndex( sxDrawable* drawable );
        
        
        // App Events
        //
        virtual void update();
        virtual void draw();
        virtual void windowResized( int w, int h );
        virtual void addEventHandler( sxApp* _app );

        // Inject Touch Events
        // 
        virtual void touchDown( float x, float y, int touchId );
        virtual void touchMove( float x, float y, int touchId );
        virtual void touchUp( float x, float y, int touchId );
        virtual void mousePress( float x, float y, int button );
        virtual void mouseDrag( float x, float y, int button );
        virtual void mouseRelease( float x, float y, int button );
              
        // OSG stuff
        //
        virtual osg::ref_ptr<osg::Camera> getOsgCamera() { return _camera; }
        virtual osg::ref_ptr<osg::PositionAttitudeTransform> getOsgXForm() { return _root; }
                
    
    protected:
        sxApp* _app;
        unsigned int _width, _height;
        
        osg::ref_ptr<osgViewer::Viewer>               _viewer;
        osg::ref_ptr<osgViewer::GraphicsWindow>       _window;
        osg::ref_ptr<osg::Camera>                     _camera;
        osg::ref_ptr<osg::PositionAttitudeTransform>  _root;
        osg::ref_ptr<osg::Geode>                      _geode;
            
        sxTouch theMouseTouch;
        std::map<int,sxTouch*>  _activeTouches;  // active touches by id, map owns the objects
        std::map<int,sxTouchHit*>  _touchHits;   // intermediary touches, sorted by zIndex
                                                 // used in rayIntersectWidgets(...)

        zIndexWidgetVisitor  _zIndexVisitor;     // Scene graph visitor, see OSG visitor pattern.
                                                 // Sets the zIndex according to graph hierachy.
                               
        void initForGlutLegacy();
        void setupPixelCamera();
        void setupLookAtCamera();                                                              
        void zIndexWidgetTraversal();
        void rayIntersectWidgets( float x, float y );
        osg::Vec3d  rayIntersectWithXYPlane(float x, float y, sxWidget* widget);
        bool getPlaneLineIntersection(const osg::Vec4d& plane, 
                                      const osg::Vec3d& lineStart, const osg::Vec3d& lineEnd, 
                                      osg::Vec3d& isect);    
        
        virtual void touchDown( sxTouch* touch );
        virtual void touchMove( sxTouch* touch );
        virtual void touchUp( sxTouch* touch );
};



#endif

